package com.example.test_2_1_transaction.controller;

import com.example.test_2_1_transaction.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@Slf4j
@RequestMapping("/trans")
@RestController
public class TransController {
    // 声明式事务
    @Autowired
    private UserService userService;
    // 正常操作没有异常
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/registry")
    public String registry(String userName, String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);

        // 当前运行阶段 事务是 提交成功的
        // 而不是回滚

        // 那么什么时候才能进行混滚呢?
        // 当所在的方法 没有抛出异常的时候 事务就提交
        // 如果抛出异常了 事务就回滚
        // 这里的抛出异常是指, 报错了, 并且没有捕获, 或者是 报错了, 捕获了, 没有处理, 重新抛出

        return "注册成功";
    }


    // 抛出异常的操作, 且没有进行异常捕获
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/r2")
    public String registry2(String userName, String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);
        int a = 10 / 0;
        return "注册成功";
        // 这时候  Transactional 就没有帮我们进行提交 , 然后就进行了 回滚
        // 事务没有进行提交
    }


    // 抛出异常的操作, 进行了异常捕获 (运行时异常)
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/r3")
    public String registry3(String userName, String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);
        try {
            int a = 10 / 0;
        }catch (Exception e){
            log.info("程序出错");
        }
        return "注册成功";
        // 这时候  如果 处理异常的话
        // 运行结果 就会成功
        // 并且事务进行了提交
        // 这就是 r3 和 r2 的区别


    }
    // Transactional 看的是 程序 程序有没有异常, 没有异常就 直接提交, 有异常了就回滚, 不管里面的代码是怎么写的
    // Transactional 也是 AOP 实现的一种体现,


    // 程序报错, 但是重新抛出异常 (事务还是回滚的)
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/r4")
    public String registry4(String userName, String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);
        try {
            int a = 10 / 0;
        }catch (Exception e){
            log.info("程序出错");
            throw  e;
        }
        return "注册成功";
        // 这时候就进行了 回滚, transactional 就没有进行提交
    }

    // 程序报错, 想手动让事务回滚, 不想进行抛出异常了
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/r5")
    public String registry5(String userName, String password){
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);
        try {
            int a = 10 / 0;
        }catch (Exception e){
            log.info("程序出错");
            // 可以通过手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 把当前事务回滚
            // currentTransactionStatus 获取当前的事务
            // setRollbackOnly 设置为回滚
        }
        return "注册成功";
        // 这时候 返回了 注册成功, 没有进行提交, 进行了回滚
    }


    // 程序报错, 如果抛出的是其他类型的异常
    @Transactional // 声明式事务 加上这个注解之后, 就自动知道什么时候提交什么时候回滚了
    @RequestMapping("/r6")
    public String registry6(String userName, String password) throws IOException {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功, result:"+result);
        try {
            int a = 10 / 0;
        }catch (Exception e){
            log.info("程序出错");
            // throw new IOException();
            throw new NullPointerException();
        }
        return "注册成功";
        // 运行之后, 程序报错了
        // 但是 事务还是被提交了 , 而事务没有进行回滚
        // 但是 换成 其他类型的异常 结果报错了, 但是事务不会提交

        // 总结:  如果 抛出的异常是 IOException 类型的话, 事务提交, 并且异常
        //          换成抛出其他类型的异常 程序异常, 但事务进行回滚, 事务不提交
        // 这是因为: Transactional 只有 遇到 运行时 异常 才会进行回滚
    }

    @Transactional(rollbackFor = {Exception.class, Error.class}, isolation = Isolation.DEFAULT)
    // 对所有异常进行回滚 , 并设置隔离级别
    @RequestMapping("/r7")
    public String registry7(String userName, String password) throws IOException {
        Integer result = userService.insertUser(userName, password);
        log.info("用户插入成功, result:" + result);
        try {
            int a = 10 / 0;
        } catch (Exception e) {
            log.info("程序出错");
            throw new IOException(); // 这种情况事务 依然会提交,
            // 但是如果我们不想让其提交
            // 只需要 在 注解里面 加上 rollbackFor 属性, 设置条件就行
        }
        return "注册成功";
    }

}
